using System;
using System.Collections.Generic;
using Firestarter.DB.Schema.BO;
using Habanero.Base;

namespace Firestarter.DM_DB.Logic
{
    /// <summary>
    /// This is a factory class for creating Database Schema objects based on a ClassDef and its related Classes e.g. PropDef, RelationshipDef.
    /// </summary>
    public class DBFactory
    {
        public IDMTypeToDBColumnTypeMapper DMTypeToDBColumnTypeMapper { get; private set; }

        public DBFactory(IDMTypeToDBColumnTypeMapper typeMapper)
        {
            if (typeMapper == null) throw new ArgumentNullException("typeMapper");
            DMTypeToDBColumnTypeMapper = typeMapper;
        }

        public DBColumn CreateDBColumn(IPropDef propDef)
        {
            DBColumn dbColumn = new DBColumn(propDef.DatabaseFieldName);
            dbColumn.Comment = propDef.Description;
            dbColumn.Nullable = !propDef.Compulsory;
            dbColumn.IsAutoIncrementing = propDef.AutoIncrementing;
            dbColumn.DataType = DMTypeToDBColumnTypeMapper.GetDBColumnType(propDef.PropertyTypeAssemblyName,
                                                                             propDef.PropertyTypeName);
            return dbColumn;
        }

        public DBPrimaryKey CreateDBPrimaryKey(IClassDef classDef, DBTable dbTable)
        {
            DBPrimaryKey dbPrimaryKey = null;
            IPrimaryKeyDef primaryKeyDef = classDef.PrimaryKeyDef;
            if (primaryKeyDef != null)
            {
                dbPrimaryKey = new DBPrimaryKey();
                for (int i = 0; i <= primaryKeyDef.Count - 1; i++)
                {
                    IPropDef propDef = primaryKeyDef[i];
                    dbPrimaryKey.AddColumnInfo(dbTable.GetColumn(propDef.DatabaseFieldName));
                }
            }
            return dbPrimaryKey;
        }

        public DBTable CreateDBTable(IClassDef classDef)
        {
            DBTable dbTable = new DBTable(classDef.TableName);
            foreach (var propDef in classDef.PropDefcol)
            {
                dbTable.Columns.Add(this.CreateDBColumn(propDef));
            }
            dbTable.PrimaryKeyDB = this.CreateDBPrimaryKey(classDef, dbTable);
            foreach (var keyDef in classDef.KeysCol)
            {
                dbTable.Keys.Add(this.CreateDBKey(keyDef, dbTable));
            }
            return dbTable;
        }

        public DBKey CreateDBKey(IKeyDef keyDef, DBTable table)
        {
            DBKey key = new DBKey();
            foreach (IPropDef uniqueConstraintProperty in keyDef)
            {
                string dbColumnName = uniqueConstraintProperty.DatabaseFieldName;
                key.AddColumnInfo(table.GetColumn(dbColumnName));
            }
            key.KeyName = keyDef.KeyName;
            return key;
        }

        public DBForeignKey CreateDBForeignKey(IRelationshipDef relationshipDef, DBTable ownerTable, DBTable relatedTable)
        {
            DBForeignKey key = new DBForeignKey();
            foreach (IRelPropDef relPropDef in relationshipDef.RelKeyDef)
            {
                string ownerColumnName = relPropDef.OwnerPropDef.DatabaseFieldName;
                IClassDef relatedClassDef = relationshipDef.RelatedObjectClassDef;
                IPropDef relatedPropDef = relatedClassDef.GetPropDef(relPropDef.RelatedClassPropName);
                key.AddColumnInfo(ownerTable.GetColumn(ownerColumnName),
                                  relatedTable.GetColumn(relatedPropDef.DatabaseFieldName));
            }
            key.ParentTable = ownerTable;
            key.RelatedTable = relatedTable;
            return key;
        }

        public void CreateDBForeignKeys(List<DBTable> dbTables, IEnumerable<IClassDef> dmClasses)
        {
            foreach (var @class in dmClasses)
            {

                foreach (IRelationshipDef dmRelationship in @class.RelationshipDefCol)
                {

                    if (!dmRelationship.OwningBOHasForeignKey) continue;
                    CreateDBForeignKey(dmRelationship, dbTables);
                }
            }

        }

        private void CreateDBForeignKey(IRelationshipDef dmRelationship, List<DBTable> dbTables)
        {
/*            var ownerTableName = dmRelationship.OwnerClass.MappedTableName;
            var relatedTableName = dmRelationship.RelatedClass.MappedTableName;*/

            var ownerTableName = dmRelationship.OwningClassDef.TableName;
            var relatedTableName = dmRelationship.RelatedObjectClassDef.TableName;
            DBTable ownerTable = dbTables.Find(table => table.TableName == ownerTableName);
            DBTable relatedTable = dbTables.Find(table => table.TableName == relatedTableName);
            var dbForeignKey = this.CreateDBForeignKey(dmRelationship,ownerTable,relatedTable);
            ownerTable.ForeignKeys.Add(dbForeignKey);
        }

        public DBDatabase CreateDBDatabase(IEnumerable<IClassDef> dmclasses)
        {
            List<DBTable> tables = new List<DBTable>();
            foreach (var @class in dmclasses)
            {
                tables.Add(this.CreateDBTable(@class));
            }
            this.CreateDBForeignKeys(tables, dmclasses);
            DBDatabase database = new DBDatabase();
            tables.ForEach(table => database.Tables.Add(table));
            return database;
        }
    }
}